const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const devBaseConfig = require("./webpack.dev.config.js");
const proBaseConfig = require("./webpack.pro.config.js");

const webpackMerge = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader/dist/index");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

const Components = require("unplugin-vue-components/webpack");
const { ElementPlusResolver } = require("unplugin-vue-components/resolvers");

function modeWebpackConfig(mode, _, args, vueConfig) {
    if (mode === "development") return devBaseConfig(_, args, vueConfig);
    else return proBaseConfig(_, args, vueConfig);
}

async function readVueConfig(args) {
    let local = null;
    if (fs.existsSync("./vue.config.js")) {
        const localConfig = require("../vue.config.js");
        const typeofLocalConfig = Object.prototype.toString.call(localConfig);
        if (typeofLocalConfig === "[object AsyncFunction]") {
            local = await localConfig(args);
        } else if (typeofLocalConfig === "[object Function]") {
            local = localConfig(args);
        } else if (typeofLocalConfig === "[object Object]") {
            local = localConfig;
        } else {
            throw new Error("Missing Config Parameter In Vue.config.js...");
        }
    }
    return local;
}

async function handleElectronMainPath(vueConfig) {
    if (!fs.existsSync(path.resolve("./package.json")))
        throw new Error(
            "the project must have to be package.json in root path..."
        );

    const packageJson = require("../package.json");
    if (!vueConfig || !vueConfig?.electronConfig) return false;

    if (typeof vueConfig.electronConfig !== "object")
        throw new Error("Missing Electron Config...");

    if (
        vueConfig.electronConfig.mainFile &&
        packageJson.main !== vueConfig.electronConfig.mainFile
    ) {
        packageJson.main = vueConfig.electronConfig.mainFile;
        fs.writeFileSync(
            path.resolve("./package.json"),
            JSON.stringify(packageJson, null, 4),
            { encoding: "utf-8" }
        );
    }
    return packageJson;
}

function installElectron() {
    try {
        require("electron");
    } catch (e) {
        console.log(
            "you project open electron technology, installing electron..."
        );
        const existsYarnLock = fs.existsSync("./yarn.lock");
        const command = existsYarnLock
            ? "yarn add electron"
            : "npm install electron";
        execSync(command, {
            cwd: "./",
            stdio: [0, 1, 2],
        });
    }
}

async function webpackConfig(_, args) {
    const mode = args.mode;
    const vueConfig = await readVueConfig(args);

    const modeConfig = modeWebpackConfig(mode, _, args, vueConfig);
    const baseConfig = webpackBaseConfig(_, args, mode);

    const mergeBaseModeConfig = webpackMerge.merge(baseConfig, modeConfig);
    const mergeWebpackConfig = webpackMerge.merge(
        mergeBaseModeConfig,
        vueConfig?.webpackConfig || {}
    );

    process.env.mode = mode;
    process.env.appTitle = vueConfig.title;
    process.env.mergeWebpackConfig = JSON.stringify(mergeWebpackConfig || {});

    if (vueConfig && vueConfig.electronConfig) {
        await handleElectronMainPath(vueConfig);
        process.env.electronConfig = JSON.stringify(vueConfig.electronConfig);
        installElectron();
    }

    return mergeWebpackConfig;
}

function webpackBaseConfig(_, args, mode) {
    return {
        mode: "production",
        entry: path.resolve("./view/main.js"),
        output: {
            path: path.resolve("./dist"),
        },
        module: {
            rules: [
                {
                    test: /\.vue$/,
                    use: ["vue-loader"],
                },
                {
                    test: /\.css$/,
                    oneOf: [
                        {
                            resourceQuery: /module/,
                            use: [
                                "vue-style-loader",
                                {
                                    loader: "css-loader",
                                    options: { modules: true },
                                },
                            ],
                        },
                        {
                            use: ["vue-style-loader", "css-loader"],
                        },
                    ],
                },
                {
                    test: /\.less$/,
                    oneOf: [
                        {
                            resourceQuery: /module/,
                            use: [
                                "vue-style-loader",
                                {
                                    loader: "css-loader",
                                    options: {
                                        modules: true,
                                        importLoaders: 1,
                                    },
                                },
                                "postcss-loader",
                                "less-loader",
                            ],
                        },
                        {
                            use: [
                                "vue-style-loader",
                                {
                                    loader: "css-loader",
                                    options: { importLoaders: 1 },
                                },
                                "postcss-loader",
                                "less-loader",
                            ],
                        },
                    ],
                },
                {
                    test: /\.jsx?$|\.tsx?$/i,
                    exclude: /node_modules/,
                    use: ["babel-loader"],
                },
                {
                    test: /\.(svg|jpg|jpeg|png)$/i,
                    type: "asset/resource",
                    generator: {
                        filename: "assets/[hash][ext][query]",
                    },
                },
            ],
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: path.resolve("./assets/index.html"),
            }),
            new VueLoaderPlugin(),
            new CleanWebpackPlugin(),
            Components({
                resolvers: [ElementPlusResolver()],
            }),
        ],
        resolve: {
            extensions: [".js", ".jsx", ".ts", ".tsx", ".vue"],
            mainFiles: ["index", "default"],
        },
    };
}

module.exports = webpackConfig;
